﻿using System;
using System.Windows;
using Tiny.Imaging.Filter.PixelLibrary;

namespace Tiny.Imaging.Filter.PointFilter
{
    public class VignetteFilter : BaseFilter.BaseFilter
    {
        private readonly double _end;

        private readonly double _end2;
        private readonly int[][] _mapping;
        private readonly int _pixelHeight;
        private readonly int _pixelWidth;
        private readonly double _size;
        private PixelPoint _center;

        private int? _factor;

        private PixelPoint _previousPoint;

        public VignetteFilter(int width, int height, int percent, double strength = 60, Func<int, int, int> blend = null, int opacity = 100)
            : base(blend, opacity)
        {
            _pixelWidth = width;
            _pixelHeight = height;
            Strength = strength / 100.0;

            _size = Math.Min(width, height) * percent / 100.0;
            _center = new PixelPoint(width / 2, height / 2);
            double start = MathLibrary.MathLibrary.Sqrt(_center.X * _center.X + _center.Y * _center.Y);
            _end = start - _size;
            _end2 = _end * _end;

            double[] bezier = PixelUtil.BezierMapping(new Point(0, 1), new Point(30, 30), new Point(70, 60),
                                                      new Point(100, 80));
            _mapping = new int[bezier.Length][];
            for (int i = 0; i < bezier.Length; ++i)
            {
                bezier[i] = Math.Max(1, bezier[i] / 10 * Strength);
                _mapping[i] = new int[256];
                for (int color = 0; color < 256; ++color)
                {
                    _mapping[i][color] = (int)(Math.Pow(color / 255.0, bezier[i]) * 255);
                }
            }
            _previousPoint = new PixelPoint(-1, -1);
        }

        public double Strength { get; set; }

        public override int[] Apply(int[] pixels)
        {
            var results = new int[pixels.Length];

            for (int x = 0; x <= _pixelWidth / 2; ++x)
            {
                for (int y = 0; y <= _pixelHeight / 2; ++y)
                {
                    foreach (int idx in PixelUtil.CoordIndexMapping(x, y, _pixelWidth, _pixelHeight))
                    {
                        results[idx] = Process(new Pixel(x, y, Rgba.Parse(pixels[idx]))).ToInteger();
                    }
                }
            }

            return results;
        }

        public override int Process(Rgba pixel)
        {
            throw new NotImplementedException();
        }

        private Rgba Process(Pixel pixel)
        {
            PixelPoint currentPoint = pixel.Position;
            Rgba rgba = pixel.Color;
            if (_previousPoint.X != currentPoint.X || _previousPoint.Y != currentPoint.Y)
            {
                _previousPoint = currentPoint;

                int dx = _center.X - currentPoint.X;
                int dy = _center.Y - currentPoint.Y;

                float dist = dx * dx + dy * dy;

                if (dist > _end2)
                {
                    var value = (int)Math.Round((MathLibrary.MathLibrary.Sqrt(dist) - _end) / _size * 100);
                    //this.factor = Math.Max(1, ((this.bezier[(int)Math.Round(((dist - this.end) / this.size) * 100)] / 10) * this.Strength));
                    _factor = value;
                }
                else
                {
                    _factor = null;
                }
            }

            return ProcessFilter(rgba, _factor);
        }

        private Rgba ProcessFilter(Rgba rgba, int? factor)
        {
            if (factor != null)
            {
                rgba.R = _mapping[factor.Value][rgba.R];
                rgba.G = _mapping[factor.Value][rgba.G];
                rgba.B = _mapping[factor.Value][rgba.B];
            }

            return rgba;
        }
    }
}